/*
 * Package il.ac.biu.cs.grossmm.impl.activeData
 * File ListSet.java
 * 
 * This is a part of presence service framework prototype. 
 * See javadoc for more information.
 *  
 * Copyright (C) 2006 Grossmann Mark
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package il.ac.biu.cs.grossmm.impl.activeData;


import il.ac.biu.cs.grossmm.api.data.Node;
import il.ac.biu.cs.grossmm.api.data.UpdateType;

import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;

/**
 * Provides a set view of a <tt>LinkedNodeset</tt> for a given
 * update type
 * 
 * @param <N>
 */
public class ListSet<N> implements Set<Node<N>> {
	LinkedNodeset nodeset;

	LinkedNode head;

	UpdateType updateType;

	boolean even;

	int stage;

	int size = -1; // to be computed on request

	public ListSet(LinkedNodeset nodeset, UpdateType updateType, int stage) {
		this.nodeset = nodeset;
		this.stage = stage;
		this.even = nodeset.even ^ stage == 0;
		this.updateType = updateType;

		switch (updateType) {
		case CHANGED:
			head = even ? nodeset.c0 : nodeset.c1;
			break;
		case ADDED:
			head = even ? nodeset.a0 : nodeset.a1;
			break;
		case REMOVED:
			head = even ? nodeset.r0 : nodeset.r1;
			break;
		case UNCHANGED:
			head = even ? nodeset.u0 : nodeset.u1;
			break;
		}
	}

	class Iter implements Iterator<Node<N>> {
		LinkedNode next;

		public boolean hasNext() {
			return next != head;
		}

		public Node<N> next() {
			if (next == null)
				if (head == null)
					throw new NoSuchElementException();
				else
					next = head;
			else if (even)
				next = next.next0;
			else
				next = next.next1;

			return next;
		}

		public void remove() {
			throw new UnsupportedOperationException();
		}

	}

	public boolean add(Node<N> e) {
		throw new UnsupportedOperationException();
	}

	public boolean addAll(Collection<? extends Node<N>> c) {
		throw new UnsupportedOperationException();
	}

	public void clear() {
		throw new UnsupportedOperationException();
	}

	public boolean contains(Object o) {
		if (!(o instanceof LinkedNode) || o == null)
			return false;

		LinkedNode n = (LinkedNode) o;

		return n.parentSet == nodeset
				&& (stage == 0 ? n.curUpdType : n.newUpdType) == updateType;
	}

	public boolean containsAll(Collection<?> c) {
		for (Object o : c)
			if (!contains(o))
				return false;
		return true;
	}

	public boolean isEmpty() {
		return head == null;
	}

	public Iterator<Node<N>> iterator() {
		return new Iter();
	}

	public boolean remove(Object o) {
		throw new UnsupportedOperationException();
	}

	public boolean removeAll(Collection<?> c) {
		throw new UnsupportedOperationException();
	}

	public boolean retainAll(Collection<?> c) {
		throw new UnsupportedOperationException();
	}

	public int size() {
		if (size >= 0)
			return size;

		if (head == null)
			size = 0;
		else {
			size = 0;
			LinkedNode n = head;
			do {
				size++;
				n = even ? n.next0 : n.next1;
			} while (n != head);
		}

		return size;
	}

	public Object[] toArray() {
		Object[] array = new Object[size()];
		int i = 0;
		for (Node<N> n : this)
			array[i++] = n;

		return array;
	}

	@SuppressWarnings("unchecked")
	public <T> T[] toArray(T[] array) {
		int i = 0;
		for (Node<N> n : this)
			array[i++] = (T) n;

		return array;
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof Set))
			return false;

		Set set = (Set) obj;

		if (size() != set.size())
			return false;

		for (Node<N> n : this)
			if (!set.contains(n))
				return false;

		return true;
	}
}
